/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/PGLObjectManager.h>
#include <pgl/core/PGLObject.h>
#include <pgl/core/PGLObjectPropPage.h>
#include <pgl/core/PGLSolidBrush.h>
#include <pgl/core/PGLLinearGradientBrush.h>
#include <pgl/core/PGLTextureBrush.h>
#include <pgl/core/PGLObjectPool.h>
#include <pgl/core/PGLDataVectors.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

PGL_IMPLEMENT_DYNCREATE(CPGLObject, CObject);
PGL_IMPLEMENT_SCRIPTING(Object);


CPGLColor CPGLObject::m_selectionColor(1.0f,0.0f,0.0f,0.8f);	//red
CPGLObject::PropertyMap CPGLObject::m_mProps;
	
//! Save settings
void CPGLObject::SerializeXML(CPGLArchive& ar)
{
	using namespace gfxinterface;

	static const TCHAR szTag[] = _T("CPGLObject");
	CPGLNodePtr pObject;

	CString str;
	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());

	// loading own data
	if (ar.IsStoring())
	{
		// odding object
		xml.AddChildElem(szTag);
		xml.IntoElem();
	
			xml.AddChildElemEx(_T("Visible"),m_bVisible);
			
			switch (m_eLayer)
			{
			case LayerFront:
				str=_T("front");
			default:
				str=_T("back");
			}
			xml.AddChildElem(_T("Layer"),str);

			xml.AddChildElem(_T("Name"),m_sName);
			xml.AddChildElem(_T("Description"),m_sDescription);

			// adding fron color
			xml.AddChildElem(_T("FrontColor"));
			xml.IntoElem();
				m_cColor.SerializeXML(ar);
			xml.OutOfElem();

			// adding fron color
			xml.AddChildElem(_T("FillBrush"));
			xml.IntoElem();
				m_pBrushFill->SerializeXML(ar);
			xml.OutOfElem();

			xml.AddChildElemEx(_T("ID"),GetID());
		
		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;
	
		xml.IntoElem();

			CPGLNode::SerializeXML( ar);

			xml.FindGetChildData(_T("Visible"),m_bVisible);

			xml.ResetChildPos();
			if (xml.FindChildElem(_T("Layer")))
			{
				str = xml.GetChildData();
				if ( str == _T("front"))
				{
					m_eLayer = LayerFront;
				}
				else
				{
					m_eLayer = LayerBack;
				}
			}

			xml.FindGetChildData(_T("Name"),m_sName);
			xml.FindGetChildData(_T("Description"),m_sDescription);
					
			xml.ResetChildPos();
			if (xml.FindChildElem(_T("FrontColor")))
			{
				xml.IntoElem();
					m_cColor.SerializeXML(ar);
				xml.OutOfElem();
			}

			xml.ResetChildPos();
			if (xml.FindChildElem(_T("FillBrush")))
			{
				xml.IntoElem();
					VERIFY(xml.SavePos(_T("ObjectPos")));
					if (xml.FindChildElem())
					{
						pObject=pgl::CObjectRegistrySingleton::GetObject(xml.GetChildTagName());
						if (pObject)
						{
							if (pObject->IsKindOf(RUNTIME_CLASS(CPGLBrush)))
							{										
								m_pBrushFill=boost::shared_polymorphic_cast<CPGLBrush,CObject>( pObject );
								VERIFY(xml.RestorePos(_T("ObjectPos")));
								m_pBrushFill->SerializeXML(ar);
							}
						}
					}
				xml.OutOfElem();
			}

		xml.OutOfElem();

		PostUpdateExtent();
	}
}


#ifdef _DEBUG
void CPGLObject::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CObject::Dump( dc );

    // now do the stuff for our specific class
	// now dumping
	dc << _T("CPGLOBject m_uID ") << GetID() << "\n";
}

void CPGLObject::AssertValid() const
{
    // call inherited AssertValid first
    CObject::AssertValid();

    // check CPerson members...
	m_cColor.AssertValid();
	m_selectionColor.AssertValid();
} 

#endif

/// default constructor
CPGLObject::CPGLObject(const CPGLColor& _FrontColor, const CPGLColor& _FillColor)
: m_bSelected(false), m_bVisible(true), 
m_cColor(_FrontColor), m_eLayer(gfxinterface::LayerBack), m_hBitmap(NULL)
{ 		
	m_sName.Format(_T("ID = %u"),GetID());

#ifdef _PGL_SHAREWARE
	if (GetID()>=1000)
	{
		AfxGetApp()->ExitInstance();
	}
#endif

	PostUpdateExtent();
	PreparePropertyMap();
};


/// assignement constructor
CPGLObject& CPGLObject::operator = (const CPGLObject& o)
{
	// prevent self-assignement
	if (&o != this)
	{
		// copy the operand
		m_bSelected=o.m_bSelected;
		m_bVisible=o.m_bVisible;
		m_eLayer = o.m_eLayer;
		m_sName=_T("Copy of ")+o.m_sName;
		m_cColor=o.m_cColor;
		m_rExtent=o.m_rExtent;
		m_pParent=o.m_pParent;
		
		m_pBrushFill.reset();
		if (o.m_pBrushFill)
			m_pBrushFill = boost::shared_polymorphic_cast<CPGLBrush, CPGLNode>(o.m_pBrushFill->MakeCopy());
	}
	return *this;
}

CPGLObject::~CPGLObject()
{
	m_pParent.reset();
	m_pBrushFill.reset();

	if (m_hBitmap)
	{
		::DeleteObject(m_hBitmap);
		m_hBitmap=NULL;
	}
}

void CPGLObject::SetBrush( CPGLBrushPtr pBrush)
{	
	m_pBrushFill = pBrush;
};

void CPGLObject::AddPropertyPage(CPropertySheet *pPropSheet)
{
	CPGLObjectPropPage* propPage=new CPGLObjectPropPage(this);

	pPropSheet->AddPage(propPage);
}

HTREEITEM CPGLObject::AddPropTree(CTreeCtrl* pTree, HTREEITEM hParent)
{
	ASSERT_VALID(pTree);
	CImageList* pImgList=pTree->GetImageList(TVSIL_NORMAL);
	ASSERT_VALID(pImgList);
	ASSERT(hParent);
	// adding bitmap
	COLORREF crMask=0;
	pImgList->Add(CBitmap::FromHandle(GetBitmap()),crMask);
	// adding to tree
	return pTree->InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_PARAM  /* nMask*/, 
				GetName() /* lpszItem*/, 
				pImgList->GetImageCount()-1 /* nImage */, 
				0 /* nSelectedImage */, 
				0 /* nState */, 
				0 /* nStateMask */, 
				GetID() /*  lParam */, 
				hParent /* hParent */, 
				TVI_LAST /* hInsertAfter */);
}

void CPGLObject::PlotGfx(gfxinterface::CGfxInterface& gfx)
{	
	if (!IsVisible())
		return;

	if (!IsInLayer(gfx.GetLayer()))
		return;
	
	if (NeedUpdateExtent())
	{
		UpdateExtent(gfx);
	}

	if (m_pBrushFill)
	{
		m_pBrushFill->SetBBox(m_rExtent);
		m_pBrushFill->MakeCurrentFill(gfx);
	}
	else
		gfx.SetFillColor(1,1,1);

	MakeFrontColorCurrent(gfx);
};

void CPGLObject::PlotLegendGfx(gfxinterface::CGfxInterface& gfx, const CPGLRectD& rBox)
{
	using namespace std;
	using namespace gfxinterface;

	PlotGfx(gfx);

	gfx.PushState();
		gfx.SetLineWidth(1);
		gfx.SetDash(_T(""));
		CPGLVectorF vX(4);
		CPGLVectorF vY(4);
		vX[0]=(float)rBox.left; vX[1]=(float)rBox.right;
		vX[2]=(float)rBox.right; vX[3]=(float)rBox.left;
		vY[0]=vY[1]=(float)(rBox.bottom+rBox.GetHeight()*2/3.0);
		vY[2]=vY[3]=(float)rBox.bottom;
		gfx.DrawLineStrip(vX, vY, LineTypeSnapBottom | LineTypeFilled | LineTypeClosed);
	gfx.PopState();
};

void CPGLObject::LoadBitmap(DWORD ID_RESSOURCE)
{
	HMODULE hInst=CPGLCoreGlobal::GetInstance();
			
	ASSERT(hInst);
	if (m_hBitmap)
		::DeleteObject(m_hBitmap);

	m_hBitmap=::LoadBitmap(hInst, MAKEINTRESOURCE(ID_RESSOURCE));
}

void CPGLObject::SetSolidFillColor(const CPGLColor& col)
{
	CPGLSolidBrushPtr pSolid(new CPGLSolidBrush(col));
	m_pBrushFill = CPGLSolidBrushPtr(pSolid);
};

void CPGLObject::SetSolidFillColor(float red,float green,float blue,float alpha)
{
	CPGLColor c(red, green, blue, alpha);
	CPGLSolidBrushPtr pSolid(new CPGLSolidBrush(c));
	m_pBrushFill = CPGLSolidBrushPtr(pSolid);
};

void CPGLObject::PreparePropertyMap()
{
	using namespace std;
	static bool bLoaded(false);
	
	if (bLoaded)
		return;
	bLoaded=true;
	
	m_mProps[_T("Object.name")]=PropertyPair(ObjectPropName, _T("Name: string"));
	m_mProps[_T("Object.description")]=PropertyPair(ObjectPropDescription, _T("Description (used in legend): string"));
	m_mProps[_T("Object.color")]=PropertyPair(ObjectPropColor, _T("Stroke Color: color ( passed as a classic color name, rgb(r,g,b) or rgba(r,g,b,a))") );
	m_mProps[_T("Object.brush")]=PropertyPair(ObjectPropBrush, _T("Brush: type value\r\n\tsolid color (see Color)\r\n\tlingrad startc endc angle (ccw, degrees)\r\n\ttexture filename wrap") );
	m_mProps[_T("Object.visible")]=PropertyPair(ObjectPropVisible, _T("Visible: on - off") );
	m_mProps[_T("Object.layer")]=PropertyPair(ObjectPropLayer, _T("Layer (for animation): front - back - disabled") );
}

void CPGLObject::Set( LPCTSTR szPropAndValues )
{
	using namespace parsers;

	CCmdParser parser;

	if (!parser.Parse( szPropAndValues))
		return;

	CCmdParser::ValueMap::const_iterator it;
	for (it=parser.begin() ; it != parser.end(); ++it)
	{
		if (!it->second.IsEmpty())
			this->SetProperty(it->first, it->second);
	}
}

void CPGLObject::ParseBrushFormat( LPCTSTR szValues )
{
	parsers::CCmdParser parser;
	if (!parser.Parse(szValues))
		return;

	CString sType,s;
	if (!parser.HasKey(_T("type"), sType))
		sType=_T("solid");

	if (sType == _T("solid") && parser.HasKey(_T("c"), s))
			SetSolidFillColor( CPGLColor::StringToColor( s ) );
	else if (sType == _T("lingrad"))
	{
		double angle;
		CPGLLinearGradientBrushPtr pBrush = CPGLLinearGradientBrushPtr(new CPGLLinearGradientBrush);
		if (parser.HasKey( _T("startc"), s))
			pBrush->SetStartColor( CPGLColor::StringToColor( s ) );
		if (parser.HasKey( _T("endc"), s))
			pBrush->SetEndColor( CPGLColor::StringToColor( s ) );
		if (parser.HasKey( _T("angle"), s))
		{
			if(_stscanf( s, _T("%lf"), &angle))
				pBrush->SetAngle( angle );
		}

		m_pBrushFill = pBrush;
	}
	else if (sType == _T("texture"))
	{
		CPGLTextureBrushPtr pBrush = CPGLTextureBrushPtr(new CPGLTextureBrush);
		if (parser.HasKey(_T("src"), s))
			pBrush->Load( s , true );
		if (parser.HasKey(_T("wrap"), s))
			pBrush->SetWrapMode( pBrush->StringToWrapMode( s ) );

		m_pBrushFill = pBrush;
	}
}

bool CPGLObject::SetProperty( LPCTSTR szProp, LPCTSTR szValue)
{
	using namespace gfxinterface;

	switch( StringToObjectProp(szProp) )
	{
	case ObjectPropName:
		SetName(szValue);
		return true;
	case ObjectPropDescription:
		SetDescription(szValue);
		return true;
	case ObjectPropColor:
		SetColor( CPGLColor::StringToColor( szValue ) );
		return true;
	case ObjectPropBrush:
		ParseBrushFormat( szValue );		
		return true;
	case ObjectPropVisible:
		Show(  (_tcsicmp(szValue,_T("on"))==0 || _tcsicmp(szValue,_T("true"))==0) );
		return true;
	case ObjectPropLayer:
		if (_tcsicmp(szValue,_T("front"))==0)
			SetLayer( LayerFront );
		else if (_tcsicmp(szValue,_T("back"))==0)
			SetLayer( LayerBack );
		else if (_tcsicmp(szValue,_T("disable"))==0)
			SetLayer( LayerDisabled );
		return true;
	default:
		return false;
	}
}

bool CPGLObject::Get( LPCTSTR szProp, CString& str)
{
	using namespace gfxinterface;

	switch( StringToObjectProp(szProp))
	{
	case ObjectPropName:
		str=GetName();
		return true;
	case ObjectPropDescription:
		str=GetDescription();
		return true;
	case ObjectPropColor:
		str=_T("rgb(0,0,0)");
		return true;
	case ObjectPropBrush:
		str=_T("not implemented yet...");
		return true;
	case ObjectPropVisible:
		if (m_bVisible)
			str=_T("true");
		else 
			str=_T("false");
		return true;
	case ObjectPropLayer:
		switch(m_eLayer)
		{
		case LayerFront:
			str=_T("front");
			break;
		case LayerBack:
			str=_T("back");
			break;
		default:
			str=_T("disabled");
		}
		return true;
	default:
		return false;
	}
}
